Automatically identifying focal methods under test in unit test cases (2015)
どんなもの?
we propose an automatic approach for detecting the focal methods under test (F-MUT) in unit test cases
F-MUTs are the methods that are responsible for system state changes that are verified through assertions by test cases
hr.icon
解決したい課題
テストに対して、method under test を精度良く見つけたい。
test をコードレコメンドの例として使える
test selection のための材料になる
error localization の推測の種になる
hr.icon
先行研究と比べてどこがすごい?
名前によってmethod under test を決める手法
test の名前によって method under test を決める
brittle and applies to test cases that strictly follow naming convention
assertion の直前に呼ばれたコードを method under test とする手法
intra-procedural な data flow analysis によって method under test を見つける
a technique based on dynamic slicing to restore test case traceability links (inter-procedural ではない?) data-flow analysis と code slicing、目的が違うだけでやってること同じだと思うんだけど、(dynamic だからいいって話?)
さらに lexical analysis を組み合わせて精度をよくするぞー
問題はこれらの方法では stop-class-list (テスト対象ではないクラス一覧)を手動で作ってフィルタリングしている(そんな馬鹿な)
Another strategy analyzes call behavior before assertion statements (last call before assert - LCBA) and presumes that a test case calls a method on the unit under test right before the assertion statement. It exploits the static call graph to identify the last class called before an assert statement. This strategy fails when, right before the assert statement, there is a call to a class other than the tested class
例えば以下のテストケースでテストしたいのは、
removeProxy によって 登録した proxy が削除されること
指定したproxyが削除されて、getProxyName や retrieveProxy を使って内部状態のinspectを行い、削除されていることを確認したい
しかし last call before assesrt で method under test として報告されるのは
retrieveProxy と getProxyName
これらのメソッドが削除したproxyを返さないテストという見方もできるけど、removeProxy で proxy を削除したという見方もできてほしい
(複数のメソッドが under test になってても良いのではないか?)
code:java
// このテストがテストしたいのは、
public void testRegisterAndRemoveProxy() {
// register a proxy, remove it, then try to retrieve it
IModel model = Model.getInstance();
IProxy proxy = new Proxy("sizes", new String[]{"7","13","21"});
model.registerProxy(proxy);
// remove the proxy
IProxy removedProxy = model.removeProxy("sizes");
// assert that we removed the appropriate proxy
assertEquals(removedProxy.getProxyName() , "sizes");
// ensure that the proxy is no longer retrievable from the model
proxy = model.retrieveProxy("sizes");
assertNull("Expecting proxy is null", proxy);
}
このようなシステムの状態を変更するメソッド(focal method under test)、ここでは removeProxy を見付けられるような手法をこの研究で作ったよん
hr.icon
他の具体例
code:java
public void testEraseAll() {
ArrayTable<String, Integer, Character> table =
create("foo", 1, ’a’, "bar", 1, ’b’, "foo", 3, ’c’);
table.eraseAll(); // <- F-MUT
assertEquals(9, table.size());
assertNull(table.get("bar", 1));
assertTrue(table.containsRow("foo"));
assertFalse(table.containsValue(’a’));
}
code:java
public void testHasCommand() {
IController controller = Controller.getInstance();
controller.registerCommand("hasCommandTest", new ControllerTestCommand()); // F-MUT
assertTrue(controller.hasCommand("hasCommandTest"));
// second sub-scenario starts here
controller.removeCommand("hasCommandTest");
assertFalse(controller.hasCommand("hasCommandTest"));
}
hr.icon
技術や手法のキモはどこ?
このheuristicsを元にF-MUTを見つける
The last method invocation entailing an object state change whose effect is inspected in the oracle part of a test case is a focal method under test (F-MUT).
テストに関するクラス一覧を抽出
クラス内のメソッドのcall graphを構築し、メソッドを以下の2つに分類
mutator: stateを変更するメソッド
inspector: stateの状態を読み取るメソッド
テストを複数のサブシナリオに分割
mutator と inspector 情報を元に、各テストシナリオからF-MUTを抽出
call graph の構築
テストからのメソッド呼び出しされているクラスの各メソッドの呼び出しグラフを作成
このcall graphは外部の依存とか、依存の依存も見るのだろうか?
call graph の構築は静的なのでinterfaceの具体的な型がわからん
interface の取り扱い
code:scala
class Concrete extends Abstract {
override def abstractMethod()
}
def method(): Foo = {
val concrete = new Concrete()
concrete.abstractMethod() // Concrete.abstractMethod (それはそうでは?) (typer の時点ではわからないのかな)
}
以下のケースではどの具体型のメソッドかわからない
code:scala
def method(x: Abstract): Foo = {
x.abstractMethod() //
}
class CUT(x: Abstract) {
val x: Abstract
def method2() = {
x.abstractMethod() // これもわからん
}
}
we resolve runtime information for interface calls using the results of a search for class declarations within the system source and test code.
具体的にはどういうふうに approximate する?
inspector と mutator に分類
class の state の状態を書き換えるような処理を行なっているメソッドを mutator
class の state を書き換えずに、その状態を読み取って返すだけのものを inspector とする
Alias-aware inter-procedural data-flow analysis を実行
To realize whether a method is mutator or inspector, our approach first analyzes which object fields a method accesses in its method body. We detect an explicit state change in two situations:
(1) a class field or any object reachable from the field is on the left hand side of an assignment statement, or
(2) a class field or any object reachable from the field is a target of method invocation whose effect changes the state of the invoked object.
code:java
class Foo extends Bar {
private var x = new Hash
public Void mutator(Hash Hash) {
this.x = hash
var alias = this.x
alias = hash // alias は this.x への参照なので書き換えられるはず
// updateSomething は effect changes the state of the invoked object なので
this.x.updateSomething(hash)
}
}
一方 メソッドの引数に与えたオブジェクトの状態を変化させるような関数もあり...
テストコードの解析
テストメソッドの抽出
これはテストフレームワークによっては簡単だが...
Slicing to sub-scenarios
テストされている expression を調べる
例えば以下の例では、buildMimeMessage によって email の状態が変わったことを email.getMimeMessage からの getContentType (java の standard library のやつら) を通して assert している
code:java
public void testDefaultCharsetAppliesToTextContent() {
email.setHostName(strTestMailServer);
email.setSmtpPort(getMailServerPort());
email.setFrom("a@b.com");
email.addTo("c@d.com");
email.setSubject("test mail");
email.setCharset("ISO-8859-1");
email.setContent("test content", "text/plain");
email.buildMimeMessage();
final MimeMessage msg = email.getMimeMessage();
msg.saveChanges();
assertEquals("text/plain; charset=ISO-8859-1", msg.getContentType());
}
MimeMessage#getMimeMessage は email.getMimeMessage() で作られたインスタンスのメソッド呼び出し
getMimeMessage は Email.message を返すものなので、assert expression は Email.message
Actual asserted expression を mutate する mutator が F-MUT
上の例では actual asserted expression は Email.message で、last called mutator は Email.buildMimeMessage
hr.icon
どうやって有効だと検証した?
hr.icon
議論はある?
hr.icon
次に読むべき論文は?
code-to-test traceability の応用 (code recommendation 以外)
test-to-code traciability によってコードのテスタビリティを図る手法
失敗したテストからプログラムエラーの箇所を推測する手法
test-to-code traceability によって、テストselectionを行うための手法
debugging